package com.livepan.main.controller;

import com.alibaba.fastjson.JSONObject;
import com.livepan.main.controller.viewobject.AnnouncementVO;
import com.livepan.main.controller.viewobject.ContestProblemVO;
import com.livepan.main.controller.viewobject.ContestVO;
import com.livepan.main.controller.viewobject.SubmissionVO;
import com.livepan.server.BaseController;
import com.livepan.server.error.BusinessException;
import com.livepan.server.response.CommonReturnType;
import com.livepan.server.service.ContestService;
import com.livepan.server.service.ProblemService;
import com.livepan.server.service.SubmissionService;
import com.livepan.server.service.model.*;
import com.livepan.server.utils.ConstUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@RestController("contest")
@RequestMapping(value = "/contest")
public class ContestController extends BaseController {
    @Autowired
    private ContestService contestService;

    @Autowired
    private SubmissionService submissionService;

    @Autowired
    private ProblemService problemService;


    @RequestMapping("/getList")
    @ResponseBody
    public CommonReturnType getContestList(@RequestParam(value = "keyword", required = false) String keyword,
                                           @RequestParam(value = "page", defaultValue = "1") Integer page) throws BusinessException {
        int limit = ConstUtil.contestLimit;
        Integer offset = (page - 1) * limit;

        List<ContestModel> contestModelList = contestService.queryContestList(keyword, offset, limit);
        List<ContestVO> contestVOList = contestModelList.stream()
                .map(this::convertContestVOFromContestModel).collect(Collectors.toList());

        long now = System.currentTimeMillis();
        for (ContestVO contestVO : contestVOList) {
            if (now < contestVO.getStartTime()) {
                contestVO.setStatus(ConstUtil.contestStatus.Pending);
            } else if (now > contestVO.getEndTime()) {
                contestVO.setStatus(ConstUtil.contestStatus.Ending);
            } else {
                contestVO.setStatus(ConstUtil.contestStatus.Running);
            }
        }
        Integer contestCount = contestService.queryContestCount(keyword);

        JSONObject result = new JSONObject();
        result.put("contestCount", contestCount);
        result.put("contestList", contestVOList);
        Long time4 = System.currentTimeMillis();
        return CommonReturnType.create(result);
    }

    @RequestMapping("/detail")
    @ResponseBody
    public CommonReturnType getDetail(@RequestParam("contestId") Integer contestId)
            throws BusinessException {
        ContestModel contestModel = contestService.getContestById(contestId, getUserModelFromSession().getId());
        List<ContestProblemModel> problemList = contestService
                .getProblemList(contestId, getUserModelFromSession().getId());

        List<ContestProblemVO> contestProblemVOList = problemList.stream()
                .map(this::convertContestProblemVOFromContestProblemModel).collect(Collectors.toList());


        ContestVO contestVO = convertContestVOFromContestModel(contestModel);
        contestVO.setContestProblemList(contestProblemVOList);

        //添加用户打开该考试的时间进去
        Long userOpenContestTime = contestService
                .getUserOpenContestTime(contestId, getUserModelFromSession().getId(), 1);
        contestVO.setOpenTime(userOpenContestTime);

        contestVO.setCloseTime(contestService
                .getUserOpenContestTime(contestId, getUserModelFromSession().getId(), 2));

        return CommonReturnType.create(contestVO);
    }

    @RequestMapping("/problem")
    @ResponseBody
    public CommonReturnType getProblem(@RequestParam("contestId") Integer contestId) throws BusinessException {
        List<ContestProblemModel> problemList = contestService
                .getProblemList(contestId, getUserModelFromSession().getId());
        List<ContestProblemVO> contestProblemVOList = problemList.stream()
                .map(this::convertContestProblemVOFromContestProblemModel).collect(Collectors.toList());
        return CommonReturnType.create(contestProblemVOList);
    }

    @RequestMapping("/announcement")
    @ResponseBody
    public CommonReturnType getAnnouncement(
            @RequestParam(value = "contestId", required = false) Integer contestId)
            throws BusinessException {
        List<AnnouncementModel> announcementModelList = contestService
                .getAnnouncementList(contestId, getUserModelFromSession().getId());
        List<AnnouncementVO> announcementVOList = announcementModelList.stream()
                .map(this::convertAnnouncementVOFromAnnouncementModel).collect(Collectors.toList());
        return CommonReturnType.create(announcementVOList);
    }


    @RequestMapping("/rank")
    @ResponseBody
    public CommonReturnType getRankList(@RequestParam("contestId") Integer contestId) throws BusinessException {
        List<ContestRankModel> contestRankList = contestService.getContestRankList(contestId, getUserModelFromSession().getId());

        List<Integer> problemIdList = contestService.getContestProblemIdList(contestId, getUserModelFromSession().getId());


        JSONObject result = new JSONObject();
        result.put("problemSet", problemIdList);
        result.put("rankList", contestRankList);
        return CommonReturnType.create(result);
    }

    @RequestMapping("/createOrUpdate")
    @ResponseBody
    public CommonReturnType createOrUpdateContest(
            @RequestBody ContestModel contestModel
    ) throws BusinessException {

        ContestModel newModel = contestService.createOrUpdateContest(contestModel, getUserModelFromSession().getId());

        return CommonReturnType.create(newModel);
    }

    @RequestMapping("/openContest")
    @ResponseBody
    public CommonReturnType openContest(@RequestParam("contestId") Integer contestId)
            throws BusinessException, IOException {
        Boolean isOk = contestService.setUserOpenContestTime(contestId,
                getUserModelFromSession().getId(), 1, System.currentTimeMillis());

        return CommonReturnType.create(null);

    }

    @RequestMapping("/endContest")
    @ResponseBody
    public CommonReturnType endContest(@RequestParam("contestId") Integer contestId)
            throws BusinessException, IOException {
        //1. 标记为用户已经结束考试
        Boolean isOk = contestService.setUserOpenContestTime(contestId,
                getUserModelFromSession().getId(), 2, System.currentTimeMillis());


        //2. 如果设置用户考试结束成功，那就开始判题
        if (isOk) {
            submissionService.reJudge(contestId, getUserModelFromSession().getId(), null);
        }

        return CommonReturnType.create(null);

    }

    @RequestMapping("/timeInfo")
    @ResponseBody
    public CommonReturnType timeInfo(@RequestParam("contestId") Integer contestId)
            throws BusinessException {
        ContestModel contestModel = contestService
                .getContestById(contestId, getUserModelFromSession().getId());
        Long userOpenContestTime = contestService
                .getUserOpenContestTime(contestId, getUserModelFromSession().getId(), 1);
        Long userCloseContestTime = contestService
                .getUserOpenContestTime(contestId, getUserModelFromSession().getId(), 2);
        Map<String, Object> result = new HashMap<>();
        result.put("startTime", contestModel.getStartTime());
        result.put("endTime", contestModel.getEndTime());
        result.put("openTime", userOpenContestTime);
        result.put("closeTime", userCloseContestTime);
        return CommonReturnType.create(result);
    }


    private ContestVO convertContestVOFromContestModel(ContestModel contestModel) {
        if (contestModel == null) {
            return null;
        }
        ContestVO contestVO = new ContestVO();
        BeanUtils.copyProperties(contestModel, contestVO);
        return contestVO;
    }

    private ContestProblemVO convertContestProblemVOFromContestProblemModel(
            ContestProblemModel contestProblemModel) {
        if (contestProblemModel == null) {
            return null;
        }

        ContestProblemVO contestProblemVO = new ContestProblemVO();
        BeanUtils.copyProperties(contestProblemModel, contestProblemVO);
        return contestProblemVO;
    }

    private AnnouncementVO convertAnnouncementVOFromAnnouncementModel(AnnouncementModel announcementModel) {
        if (announcementModel == null) {
            return null;
        }

        AnnouncementVO announcementVO = new AnnouncementVO();
        BeanUtils.copyProperties(announcementModel, announcementVO);
        return announcementVO;
    }

    private SubmissionVO convertSubmissionVOFromSubmissionModel(SubmissionModel submissionModel) {
        if (submissionModel == null) {
            return null;
        }
        SubmissionVO submissionVO = new SubmissionVO();

        BeanUtils.copyProperties(submissionModel, submissionVO);

        return submissionVO;
    }
}
